import torch
import numpy as np

dev = torch.device('cpu')
print(dev)

a = torch.tensor(data = [i for i in range(5)]) # 创建Tensor类型的变量，采用值定义
b = torch.Tensor(20, 4) # 采用结构创建tensor，直接生成20X4结构的随机值
"""
Tensor的属性：
torch.dtype: date type
torch.device: the device of the constructed tensor. 
    If None and data is a tensor then the device of data is used. 
    If None and data is not a tensor then
    the result tensor is constructed on the current device.
    标识Tensor对象存储在设备的名称，如：CPU/GPU=>CUDA:0/CUDA=>1 ...
    定义tensor的device可优化网络结构效率，如对tensor进行定义时，可指定device为cpu，对tensor进行矩阵计算时，可指定device为cuda，提升效率
torch.layout:标识Tensor内存布局的对象
"""
# 定义稀疏张量的方法
indices = torch.tensor([[0,1,1], [2,1,0]]) # 定义非零元素坐标，分别(0,2)\(1,1)\(1,0)
values = torch.tensor([3,4,5]) # 定义非零元素的值
c = torch.sparse_coo_tensor(indices, values, size=[5,6]) # 生成稀疏张量
d = c.to_dense() # 转成稠密张量，即将全部元素展示出来
print(d)


a = np.array([i for i in range(5)],)
a = torch.ones(size=(2,3,4,2)) # 创建全1的tensor，类似np
"""
其他函数与np类似，如：
torch.zeros()
torch.eye()
torch.arange(start, end, step)
torch.linspace(start, end, steps)
torch.rand/randn(size, *argm)
torch.normal(mean, std)/uniform_(from, to) # 正态/均匀分布
torch.randperm(m) # 随机排列
"""
print(c)


# tensor的算数运算
# 加减乘除，以加为示例：
a = torch.tensor([1,2])
b = torch.tensor([3,4])
c = a + b # 加法
c = torch.add(a, b)
c = a.add(b)
a.add_(b) # 加法运算，后缀下划线“_”表示将相加后的值赋予a变量
# 加法：.add 减法：.sub 
# 乘法(点乘)：
c = a * b
c = torch.mul(a, b)
# 除法(元素相除)
c = a / b
c = torch.div(a, b)
# 幂运算
c = torch.pow(a, 2)
c = a.pow(2)
c = a ** 2
a.pow_(2)
c = torch.exp(a)
# 开方运算：.sqrt()
# 对数运算：.log()

# tensor的军阵运算：
# 二维矩阵的五种计算方式
'''
b = b.T
c = torch.mm(a, b)
c = torch.matmul(a, b)
c = a @ b
c = a.matmul(b)
c = a.mm(b)
'''
# 高维矩阵的计算方式
# 注：高维矩阵乘法，仅仅定义在最后两个维度上，需要求前面的维度必须保持一致，
# 高维矩阵乘法运算只有.matmul方法


# torch的in-place操作：不允许增加或使用临时变量
# x = x + y
# add_ /sub_ /mul_等有下划线的方法


# torch的广播机制：运算时，张量参数size可自动拓展为相同大小
# 广播机制需满足条件：每个张量至少有一个维度；满足右对齐
# 示例：a(2, 3, 1),b(3),c(2, 2, 2)
# 按右对齐要求，a与b配对时，b可拓展为(1, 1, 3)，因1可匹配任何整数，因此a与b可进行广播
# 按右对齐要求，b与c配对时，b可拓展为(1, 1, 3)，因最右项元素3/2不为整，不可广播
# (4, 3, 5, 6),(2, 1, 10, 3)可进行广播计算


# tensor的取整/取余
# .floor() # 向下取整
# .ceil() # 向上取整
# .round() # 四舍五入
# .trunc() # 裁剪，留整
# .frac() # 取小数
# % # 求余


# tensor的比较运算
# torch.eq(input, other, out=None) # 按元素成员进行等式比较，相同返回tensor类型的bool序列
# torch.equal(tensor1, tensor2) # 比较tensor1和tensor2是否有相同的size和elements
# torch.ge(input, other, out=None) # input >= other
# torch.gt(input, other, out=None) # input > other
# torch.le(input, other, out=None) # input <= other
# torch.lt(input, other, out=None) # input < other
# torch.ne(input, other, out=None) # input != other


# tensor的取前k大/小和第k小的数值及索引
# torch.sort(input, dim=None, descening=False, out=None) # sort
# troch.topk(input, k, dim=None, largest=True, sort=True, out=None) # 取前k值,largest:是否取最大或最小,sort:是否对返回结果进行排序
# troch.kthvalue(input, k, dim=None, out= None) # 
# #
# torch.isfinite(tensor)
# torch.isinf(tensor)
# torch.isnan(tensor)
# tensor的三角函数，与np类似
# tensor的统计函数，与np类似


# torch.distributions:包含可参数化的概率分布和采样函数
# 得分函数：强化学习中策略梯度方法的基础
# pathwise derivative评估器：变分自动编码器中的重新参数化技巧
import torch.distributions as distributions


# torch的随机抽样
# 定义随机种子：示例：torch.manual_sedd(seed)
# 定义随机数满足的分布，示例：torch.normal()


# tensor的范数
# torch.dist(input, other, p=num) # 计算num范数
# torch.norm() # 计算2范数


# tensor矩阵分解
# LU分解：上三角矩阵×下三角矩阵
# QR分解：正交矩阵×上三角矩阵
# EVD分解：特征值分解，无监督算法，例PCA分析
# SVD分解：奇异值分解，有监督算法，解决矩阵不是方阵的情况，例LDA分解
#   LDA分解目标：寻找数据新的主成分，使得同类的数据尽可能聚集在一起，不同类数据之间的距离尽可能远
# 方法
# torch.svd()


# tensor裁剪运算
# 梯度裁剪gradient clipping:发生梯度离散或者梯度爆炸时，对梯度进行处理
# tansor.clamp(min, max)


# tensor索引与筛选
# torch.where(condition, x, y) # 按条件从x和y中选出满足条件的元素并组成新tensor
# torch.gather(input, dim, index, out=None) # 在指定维度上按照索引赋值输出tensor
# torch.index_select(input, dim, index, out=None) # 按照指定索引输出tensor
# torch.masked_select(input, mask, out=None) # 按照mask输出tensor，输出为向量
# torch.take(input, indices) # 将输入看成1D-tensor，按照索引输出tensor
# torch.nonzero(input, out=None) # 输出非0元素的坐标


# tensor组合拼接
# torch.cat(seq, dim, out=None) # 按照已经存在的维度进行拼接
# torch.stack(seq, dim, out=None) # 按照新维度进行拼接
# torch.gather(input, dim, index, out=None) # 在指定维度上按照索引值赋值输出tensor


# tensor切片
# torch.chunk(tensor, chunks, dim) # 按照某个维度平均分块（最后一个可能小于均分值，有些不可整除问题）
# torch.split(tensor, split_size_or_sections, dim) # 按照某个值或[int, ]进行分割


# tensor变形操作
# torch.reshape(input, shape)
# torch.t(input) # 转置，只针对2维
# torch.transpose(input, dim0, dim1) # 交换2个维度
# torch.squeeze(input, dim, out=None) # 去除那些大小维度大小为1的维度
# torch.unbind(tensor, dim) # 去除某个维度
# torch.unsqueeze(input, dim, out=None) # 在指定位置增加维度
# torch.flip(input, dims) # 按照给定维度翻转张量
# torch.rot90(input, k, dims) # 按照指定维度和旋转次数进行张量旋转
print(torch.rand([2,1,3]))


# tensor填充
# torch.full


# tensor的频谱操作，与np类似